﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Web;

/// <summary>
/// QQWry 的摘要说明
/// </summary>
/// <summary>
///QQWry 的摘要说明
//纯真版QQ IP数据库下载:http://www.cz88.net/fox/
/// </summary>
public class QQWry
{
    //Singleton
    static QQWry instance;
    public static QQWry GetInstance(string dataPath)
    {
        if (instance == null)
            instance = new QQWry(dataPath);

        return instance;
    }


    //第一种模式
    private const byte REDIRECT_MODE_1 = 0x01;

    //第二种模式
    private const byte REDIRECT_MODE_2 = 0x02;

    //每条记录长度
    private const int IP_RECORD_LENGTH = 7;

    //数据库文件
    private FileStream ipFile;
    private string ipFile2;

    private const string unCountry = "未知国家";
    private const string unArea = "未知地区";

    //索引开始位置
    private long ipBegin;

    //索引结束位置
    private long ipEnd;

    //IP地址对象
    private QQIPLocation loc;

    //存储文本内容
    private byte[] buf;

    //存储3字节
    private byte[] b3;

    //存储4字节
    private byte[] b4;

    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="ipfile">IP数据库文件绝对路径</param>
    /// <returns></returns>
    public QQWry(string ipfile)
    {

        buf = new byte[100];
        b3 = new byte[3];
        b4 = new byte[4];
        ipFile2 = ipfile;

    }

    /// <summary>
    /// 搜索IP地址搜索
    /// </summary>
    /// <param name="ip"></param>
    /// <returns></returns>
    public QQIPLocation SearchIPLocation(string ip)
    {
        try
        {
            ipFile = new FileStream(ipFile2, FileMode.Open);

            ipBegin = readLong4(0);
            ipEnd = readLong4(4);
            loc = new QQIPLocation();


            //将字符IP转换为字节
            string[] ipSp = ip.Split('.');
            if (ipSp.Length != 4)
            {
                throw new ArgumentOutOfRangeException("不是合法的IP地址!");
            }
            byte[] IP = new byte[4];
            for (int i = 0; i < IP.Length; i++)
            {
                IP[i] = (byte)(Int32.Parse(ipSp[i]) & 0xFF);
            }

            QQIPLocation local = null;
            long offset = locateIP(IP);

            if (offset != -1)
            {
                local = getIPLocation(offset);
            }

            if (local == null)
            {
                local = new QQIPLocation();
                local.area = unArea;
                local.country = unCountry;
            }

            ipFile.Dispose();

            return local;
        }
        catch
        {
            return new QQIPLocation();
        }
        finally
        {
            ipFile.Dispose();
        }
    }

    /// <summary>
    /// 取得具体信息
    /// </summary>
    /// <param name="offset"></param>
    /// <returns></returns>
    private QQIPLocation getIPLocation(long offset)
    {
        ipFile.Position = offset + 4;
        //读取第一个字节判断是否是标志字节
        byte one = (byte)ipFile.ReadByte();
        if (one == REDIRECT_MODE_1)
        {
            //第一种模式
            //读取国家偏移
            long countryOffset = readLong3();
            //转至偏移处
            ipFile.Position = countryOffset;
            //再次检查标志字节
            byte b = (byte)ipFile.ReadByte();
            if (b == REDIRECT_MODE_2)
            {
                loc.country = readString(readLong3());
                ipFile.Position = countryOffset + 4;
            }
            else
                loc.country = readString(countryOffset);

            //读取地区标志
            loc.area = readArea(ipFile.Position);

        }
        else if (one == REDIRECT_MODE_2)
        {
            //第二种模式
            loc.country = readString(readLong3());
            loc.area = readArea(offset + 8);
        }
        else
        {
            //普通模式
            loc.country = readString(--ipFile.Position);
            loc.area = readString(ipFile.Position);
        }
        return loc;
    }

    /// <summary>
    /// 读取地区名称
    /// </summary>
    /// <param name="offset"></param>
    /// <returns></returns>
    private string readArea(long offset)
    {
        ipFile.Position = offset;
        byte one = (byte)ipFile.ReadByte();
        if (one == REDIRECT_MODE_1 || one == REDIRECT_MODE_2)
        {
            long areaOffset = readLong3(offset + 1);
            if (areaOffset == 0)
                return unArea;
            else
            {
                return readString(areaOffset);
            }
        }
        else
        {
            return readString(offset);
        }
    }

    /// <summary>
    /// 读取字符串
    /// </summary>
    /// <param name="offset"></param>
    /// <returns></returns>
    private string readString(long offset)
    {
        ipFile.Position = offset;
        int i = 0;
        for (i = 0, buf[i] = (byte)ipFile.ReadByte(); buf[i] != (byte)(0); buf[++i] = (byte)ipFile.ReadByte()) ;

        if (i > 0)
            return Encoding.Default.GetString(buf, 0, i);
        else
            return "";
    }

    /// <summary>
    /// 查找IP地址所在的绝对偏移量
    /// </summary>
    /// <param name="ip"></param>
    /// <returns></returns>
    private long locateIP(byte[] ip)
    {
        long m = 0;
        int r;

        //比较第一个IP项
        readIP(ipBegin, b4);
        r = compareIP(ip, b4);
        if (r == 0)
            return ipBegin;
        else if (r < 0)
            return -1;
        //开始二分搜索
        for (long i = ipBegin, j = ipEnd; i < j;)
        {
            m = this.getMiddleOffset(i, j);
            readIP(m, b4);
            r = compareIP(ip, b4);
            if (r > 0)
                i = m;
            else if (r < 0)
            {
                if (m == j)
                {
                    j -= IP_RECORD_LENGTH;
                    m = j;
                }
                else
                {
                    j = m;
                }
            }
            else
                return readLong3(m + 4);
        }
        m = readLong3(m + 4);
        readIP(m, b4);
        r = compareIP(ip, b4);
        if (r <= 0)
            return m;
        else
            return -1;
    }

    /// <summary>
    /// 从当前位置读取四字节,此四字节是IP地址
    /// </summary>
    /// <param name="offset"></param>
    /// <param name="ip"></param>
    private void readIP(long offset, byte[] ip)
    {
        ipFile.Position = offset;
        ipFile.Read(ip, 0, ip.Length);
        byte tmp = ip[0];
        ip[0] = ip[3];
        ip[3] = tmp;
        tmp = ip[1];
        ip[1] = ip[2];
        ip[2] = tmp;
    }

    /// <summary>
    /// 比较IP地址是否相同
    /// </summary>
    /// <param name="ip"></param>
    /// <param name="beginIP"></param>
    /// <returns>0:相等,1:ip大于beginIP,-1:小于</returns>
    private int compareIP(byte[] ip, byte[] beginIP)
    {
        for (int i = 0; i < 4; i++)
        {
            int r = compareByte(ip[i], beginIP[i]);
            if (r != 0)
                return r;
        }
        return 0;
    }

    /// <summary>
    /// 比较两个字节是否相等
    /// </summary>
    /// <param name="bsrc"></param>
    /// <param name="bdst"></param>
    /// <returns></returns>
    private int compareByte(byte bsrc, byte bdst)
    {
        if ((bsrc & 0xFF) > (bdst & 0xFFL))
            return 1;
        else if ((bsrc ^ bdst) == 0)
            return 0;
        else
            return -1;
    }

    /// <summary>
    /// 从当前位置读取4字节,转换为长整型
    /// </summary>
    /// <param name="offset"></param>
    /// <returns></returns>
    private long readLong4(long offset)
    {
        long ret = 0;
        ipFile.Position = offset;
        ret |= (ipFile.ReadByte() & 0xFFL);
        ret |= ((ipFile.ReadByte() << 8) & 0xFF00L);
        ret |= ((ipFile.ReadByte() << 16) & 0xFF0000L);
        ret |= ((ipFile.ReadByte() << 24) & 0xFF000000L);
        return ret;
    }

    /// <summary>
    /// 根据当前位置,读取3字节
    /// </summary>
    /// <param name="offset"></param>
    /// <returns></returns>
    private long readLong3(long offset)
    {
        long ret = 0;
        ipFile.Position = offset;
        ret |= (ipFile.ReadByte() & 0xFFL);
        ret |= ((ipFile.ReadByte() << 8) & 0xFF00L);
        ret |= ((ipFile.ReadByte() << 16) & 0xFF0000L);
        return ret;
    }

    /// <summary>
    /// 从当前位置读取3字节
    /// </summary>
    /// <returns></returns>
    private long readLong3()
    {
        long ret = 0;
        ret |= (ipFile.ReadByte() & 0xFFL);
        ret |= ((ipFile.ReadByte() << 8) & 0xFF00L);
        ret |= ((ipFile.ReadByte() << 16) & 0xFF0000L);
        return ret;
    }

    /// <summary>
    /// 取得begin和end中间的偏移
    /// </summary>
    /// <param name="begin"></param>
    /// <param name="end"></param>
    /// <returns></returns>
    private long getMiddleOffset(long begin, long end)
    {
        long records = (end - begin) / IP_RECORD_LENGTH;
        records >>= 1;
        if (records == 0)
            records = 1;
        return begin + records * IP_RECORD_LENGTH;
    }
}

public class QQIPLocation
{
    public String country;
    public String area;

    public String Prvoince
    {
        get
        {
            if (string.IsNullOrEmpty(country))
                return "";

            int pos = country.IndexOf("省");
            if (pos == -1)
                return "";

            return country.Substring(0, pos);
        }
    }

    public String City
    {
        get
        {
            if (string.IsNullOrEmpty(country))
                return "";

            int start = country.IndexOf("省");
            int pos = country.IndexOf("市");
            if (pos == -1)
                return "";

            return country.Substring(start + 1, pos - start - 1);
        }
    }

    public QQIPLocation()
    {
        country = area = "";
    }

    public QQIPLocation getCopy()
    {
        QQIPLocation ret = new QQIPLocation();
        ret.country = country;
        ret.area = area;
        return ret;
    }
}